package model;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;

import utils.Stemmer;
import edu.stanford.nlp.ling.TaggedWord;

public class TaggedSlice {
	private ArrayList<TaggedWord> tokens = new ArrayList<TaggedWord>();
	private String id;
	private final String default_delimiter = " ";

	public TaggedSlice(File file) {
		try {
			String content = readFileAsString(file, null);
			ArrayList<String> taggedWords = stringToTokens(content, null);
			for (String taggedWord : taggedWords) {
				String word = taggedWord.substring(0, taggedWord.indexOf('/'));
				String tag = taggedWord.substring(taggedWord.indexOf('/') + 1);
				tokens.add(new TaggedWord(word, tag));
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		this.id = file.getName();
	}

	public TaggedSlice(List<TaggedWord> tokens) {
		this.tokens.addAll(tokens);
	}

	/**
	 * Returns a document vector consisting of words and counts
	 * 
	 * @return A map with word as the key and count as the value
	 */
	public Map<String, Integer> toWords(boolean ignoreCase, boolean justNoun,
			boolean enableStemming) {
		HashMap<String, Integer> words = new HashMap<String, Integer>();
		for (TaggedWord token : tokens) {
			String word = token.word();
			if (ignoreCase) {
				word = word.toLowerCase();
			}
			if (isAlphabetic(word)) {
				if (justNoun
						&& !(token.tag().equals("NN") || token.tag().equals(
								"NNS"))) {
					continue;
				}
				if (enableStemming) {
					Stemmer s = new Stemmer();
					word = s.stem(word.toLowerCase());
				}
				if (words.containsKey(word)) {
					words.put(word, words.get(word) + 1);
				} else {
					words.put(word, 1);
				}
			}
		}
		return words;
	}

	/**
	 * Getter for id
	 * 
	 * @return id of the document
	 */
	public String getId() {
		return id;
	}

	/**
	 * Parse a string into a tokenized representation
	 * 
	 * @param content
	 *            The string to be tokenized
	 * @return a ArrayList of the tokens
	 */
	private ArrayList<String> stringToTokens(String content, String delimiter) {
		ArrayList<String> tokens = new ArrayList<String>();
		StringTokenizer stk = new StringTokenizer(content,
				delimiter == null ? default_delimiter : delimiter);
		while (stk.hasMoreTokens()) {
			String token = stk.nextToken();
			tokens.add(token);
		}
		return tokens;
	}

	/**
	 * Parse a file into a tokenized representation
	 * 
	 * @param file
	 *            The file to be tokenized
	 * @return a ArrayList of the tokens
	 */
	private ArrayList<String> fileToTokens(File file) {
		String content = "";
		try {
			content = readFileAsString(file, null);
		} catch (FileNotFoundException e) {
			System.err.println("File " + file.toString() + " cannot be found.");
		} catch (IOException e) {
			e.printStackTrace();
		}
		return stringToTokens(content, null);
	}

	/**
	 * Modified from http://ptspts.blogspot.com/2009/11/how-to-read-
	 * whole-file-to-string-in.html
	 * 
	 * @param file
	 *            The file to be read
	 * @param charsetName
	 *            The charset to be used. if null, use default
	 */
	public String readFileAsString(File file, String charsetName)
			throws java.io.IOException {
		java.io.InputStream is = new java.io.FileInputStream(file);
		try {
			final int bufsize = 4096;
			int available = is.available();
			byte data[] = new byte[available < bufsize ? bufsize : available];
			int used = 0;
			while (true) {
				if (data.length - used < bufsize) {
					byte newData[] = new byte[data.length << 1];
					System.arraycopy(data, 0, newData, 0, used);
					data = newData;
				}
				int got = is.read(data, used, data.length - used);
				if (got <= 0)
					break;
				used += got;
			}
			return charsetName != null ? new String(data, 0, used, charsetName)
					: new String(data, 0, used);
		} finally {
			is.close();
		}
	}

	/**
	 * Check whether a word consists of only alphabetic symbols
	 * 
	 * @param word
	 * @return
	 */
	public boolean isAlphabetic(String word) {
		for (int i = 0; i < word.length(); i++) {
			char c = word.charAt(i);
			if (c < 'A' || c > 'z') {
				return false;
			}
		}
		return true;
	}
}
